home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Devices and Hardware / Disks / BasicDiskImage / MoreIsBetterBits / TradDriverLoaderLib.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  23.0 KB  |  804 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TradDriverLoaderLib.c
  3.  
  4.     Contains:    Implementation for the pseudo-DriverLoaderLib for 'DRVR's.
  5.  
  6.     Written by:    Quinn
  7.  
  8.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.  
  20.          <1>     25/2/99    Quinn   First checked in.
  21. */
  22.  
  23. /////////////////////////////////////////////////////////////////
  24. // MoreIsBetter Setup
  25.  
  26. #include "MoreSetup.h"
  27.  
  28. /////////////////////////////////////////////////////////////////
  29. // Mac OS Interfaces
  30.  
  31. #include <LowMem.h>
  32. #include <DriverGestalt.h>
  33. #include <TextUtils.h>
  34.  
  35. // Switched from using:
  36. //
  37. //   #include <PLStringFuncs.h>
  38. //
  39. // to using BlockMoveData because it's so hard to get PLstrcpy working
  40. // across a zillion different compilers.  *sigh*
  41.  
  42. /////////////////////////////////////////////////////////////////
  43. // MIB Prototypes
  44.  
  45. #include "MoreInterfaceLib.h"
  46.  
  47. /////////////////////////////////////////////////////////////////
  48. // Our Prototypes
  49.  
  50. #include "TradDriverLoaderLib.h"
  51.  
  52. ///////////////////////////////////////////////////////////////////////////
  53.  
  54. extern pascal SInt16 TradHigherDriverVersion(NumVersion *dv1, NumVersion *dv2)
  55. {
  56.     UInt16 nonRelRev1, nonRelRev2;
  57.  
  58.     if (dv1->majorRev           > dv2->majorRev)        return  1;
  59.     if (dv1->majorRev           < dv2->majorRev)        return -1;
  60.     if (dv1->minorAndBugRev     > dv2->minorAndBugRev)  return  1;
  61.     if (dv1->minorAndBugRev     < dv2->minorAndBugRev)  return -1;
  62.     if (dv1->stage              > dv2->stage)           return  1;
  63.     if (dv1->stage              < dv2->stage)           return -1;
  64.  
  65.     nonRelRev1 = dv1->nonRelRev;
  66.     nonRelRev2 = dv2->nonRelRev;
  67.     
  68.     if (dv1->stage == finalStage) {
  69.         if (dv1->nonRelRev == 0)                 nonRelRev1 = 0xFFFF;
  70.         if (dv2->nonRelRev == 0)                 nonRelRev2 = 0xFFFF;
  71.     }
  72.  
  73.     if (nonRelRev1 > nonRelRev2)                        return  1;
  74.     if (nonRelRev1 < nonRelRev2)                        return -1;
  75.  
  76.     return 0;
  77. }
  78.  
  79. ///////////////////////////////////////////////////////////////////////////
  80.  
  81. extern pascal UnitNumber TradHighestUnitNumber(void)
  82.     // See comment in header file.
  83. {
  84.     return (MoreLMGetUnitTableEntryCount() - 1);
  85. }
  86.  
  87. ///////////////////////////////////////////////////////////////////////////
  88.  
  89. extern pascal Boolean TradDriverGestaltIsOn(DriverFlags flags)
  90.     // See comment in header file.
  91. {
  92.     return ( (flags & kmDriverGestaltEnableMask) != 0 );
  93. }
  94.  
  95. ///////////////////////////////////////////////////////////////////////////
  96.  
  97. static OSErr DriverGestaltOnOff(DriverRefNum refNum, Boolean setIt)
  98.     // This routine is called by TradDriverGestaltOn and
  99.     //  TradDriverGestaltOff to either set or clear the
  100.     //  kmDriverGestaltEnableMask bit in the DCE flags.
  101. {
  102.     OSErr err;
  103.     AuxDCEHandle thisDCE;
  104.     
  105.     // First called TradGetDriverInformation to validate the refNum
  106.     //  and verify that the driver exists.
  107.     err = TradGetDriverInformation(refNum, nil, nil, nil, nil);
  108.     if (err == noErr) {
  109.         thisDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  110.         if (setIt) {
  111.             (**thisDCE).dCtlFlags |= kmDriverGestaltEnableMask;
  112.         } else {
  113.             (**thisDCE).dCtlFlags &= ~kmDriverGestaltEnableMask;
  114.         }
  115.     }
  116.     
  117.     return err;
  118. }
  119.  
  120. ///////////////////////////////////////////////////////////////////////////
  121.  
  122. extern pascal OSErr TradDriverGestaltOn(DriverRefNum refNum)
  123.     // See comment in header file.
  124. {
  125.     return DriverGestaltOnOff(refNum, true);
  126. }
  127.  
  128. ///////////////////////////////////////////////////////////////////////////
  129.  
  130. extern pascal OSErr TradDriverGestaltOff(DriverRefNum refNum)
  131.     // See comment in header file.
  132. {
  133.     return DriverGestaltOnOff(refNum, false);
  134. }
  135.  
  136. ///////////////////////////////////////////////////////////////////////////
  137.  
  138. extern pascal OSErr TradOpenInstalledDriver(DriverRefNum refNum, SInt8 ioPermission)
  139.     // See comment in header file.
  140. {
  141.     OSErr                 err;
  142.     Str255                driverName;
  143.     DriverRefNum    realRefNum;
  144.  
  145.     // Check parameters.
  146.     err = noErr;
  147.     if (ioPermission != fsRdWrPerm) {
  148.         err = paramErr;
  149.     }
  150.     
  151.     // Get the name of the driver, then simply open it.
  152.     if (err == noErr) {
  153.         err = TradGetDriverInformation(refNum, nil, nil, driverName, nil);
  154.     }
  155.     if (err == noErr) {
  156.         if ( driverName[0] == 0 ) {
  157.             err = paramErr;
  158.         }
  159.     }
  160.     if (err == noErr) {
  161.         err = OpenDriver(driverName, &realRefNum);
  162.     }
  163.     if (err == noErr) {
  164.         if (realRefNum != refNum) {
  165.             err = paramErr;        // My favourite error code -- at some intrinsic level, every error is a paramErr (-;
  166.         }
  167.     }
  168.     
  169.     return err;
  170. }
  171.  
  172. ///////////////////////////////////////////////////////////////////////////
  173.  
  174. extern pascal OSErr TradLookupDrivers(UnitNumber beginningUnit,
  175.                                         UnitNumber endingUnit,
  176.                                         Boolean emptyUnits,
  177.                                         ItemCount *returnedRefNums, 
  178.                                         DriverRefNum *refNums)
  179.     // See comment in header file.
  180. {
  181.     OSErr err;
  182.     AuxDCEHandle     *unitTable;
  183.     ItemCount         maxRefNums;
  184.     UnitNumber         currentUnit;
  185.     
  186.     // Sanity check the parameters.
  187.     if ( endingUnit > TradHighestUnitNumber() ) {
  188.         endingUnit = TradHighestUnitNumber();
  189.     }
  190.     err = noErr;
  191.     if ( beginningUnit > TradHighestUnitNumber() ) {
  192.         err = badUnitErr;
  193.     }
  194.     if (err == noErr) {
  195.         if (beginningUnit > endingUnit ) {
  196.             err = paramErr;
  197.         }
  198.     }
  199.  
  200.     // Now do the real work...
  201.     if (err == noErr) {
  202.         unitTable = (AuxDCEHandle *) LMGetUTableBase();
  203.  
  204.         maxRefNums = *returnedRefNums;
  205.         
  206.         // Loop through each unit table entry from beginningUnit to endingUnit inclusive.
  207.         *returnedRefNums = 0;
  208.         currentUnit = beginningUnit;
  209.         while ( currentUnit <= endingUnit ) {
  210.  
  211.             // If we've still got space to return a unit...
  212.             if ( *returnedRefNums < maxRefNums ) {
  213.             
  214.                 // and we're interested in this unit...
  215.                 if (    (emptyUnits && unitTable[currentUnit] == nil) ||
  216.                             (!emptyUnits && unitTable[currentUnit] != nil) ) {
  217.                     
  218.                     // then copy the unit out to the caller's array
  219.                     refNums[*returnedRefNums] = ~currentUnit;
  220.                     *returnedRefNums += 1;
  221.                 }
  222.             }
  223.             currentUnit += 1;
  224.         }
  225.     
  226.     }
  227.     
  228.     return err;
  229. }
  230.  
  231. ///////////////////////////////////////////////////////////////////////////
  232.  
  233. enum {
  234.     kNoUnitNumber = 0xFFFF
  235. };
  236.  
  237. static UnitNumber IsDriverInstalled(ConstStr255Param name, UnitNumber skipThisUnit)
  238.     // Look through the unit table to see if there is a driver with this name
  239.     //  already installed.  Note that you might consider calling OpenDriver
  240.     //  here, but that would be wrong.  OpenDriver has similar semantics, but
  241.     //  if it fails to find a driver in the unit table it will search the
  242.     //  current resource chain looking for a DRVR resource to install.
  243.     //  Given that it's likely our client has a DRVR resource in their
  244.     //  resource chain ('cause they're messing around trying to install
  245.     //  drivers), and that OpenDriver will install it without detaching
  246.     //  it from the client's resource file, and that the client's
  247.     //  resource file may go away (ie they're a DropMounter-like application
  248.     //  or some INIT running at system startup), this would be bad.
  249. {
  250.     UnitNumber    endingUnit;
  251.     UnitNumber    unit;
  252.     Str255            unitName;
  253.     
  254.     endingUnit = TradHighestUnitNumber();
  255.     
  256.     for (unit = 0; unit <= endingUnit; unit++) {
  257.         if ( TradGetDriverInformation(~unit, nil, nil, unitName, nil) == noErr) {
  258.             if ( unit != skipThisUnit && EqualString(name, unitName, false, true) ) {
  259.                 return unit;
  260.             }
  261.         }
  262.     }
  263.     
  264.     return kNoUnitNumber;
  265. }
  266.  
  267. ///////////////////////////////////////////////////////////////////////////
  268.  
  269. enum {
  270.     kMaximumNumberOfUnitTableEntries = 1024,
  271.     // kMaximumNumberOfUnitTableEntries = 8000,
  272.     
  273.     // kMaximumNumberOfUnitTableEntries is documented in Technote
  274.     //  DV 23 "Driver Education" <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  275.     //  as being the maximum size that the classic Device Manager
  276.     //  would grow the unit table.  In theory, this limits the system
  277.     //  to 128 unit table entries.
  278.     // However the traditional Mac OS is capable of dealing with much more
  279.     //  than 128 units.  In fact, some multi-port serial card vendors
  280.     //  regularly install more.
  281.     // Prior to Mac OS 8, the PCI DriverLoaderLib enforced the 128 limit.
  282.     //  I filed a bug against the PCI DriverLoaderLib to get this limit
  283.     //  raised, and the new limit under Mac OS 8 is 1024.
  284.     // Given that official sanction, I have now raised the standard
  285.     //  limit enforced by this library to 1024.  I supply an alternative
  286.     //  maximum (8000), designed to keep the unit table smaller than 32K.
  287.     //  This is important because many people use 68K word indexing
  288.     //  (ie x(a0,d0.w) to to access the entries.
  289.     // I have tested TradDriverLoaderLib installing up to 500 device
  290.     //  drivers.  
  291.     
  292.     kNumberOfEntriesToGrowUnitTable = 4
  293.     
  294.     // Technote DV 23 "Driver Education"
  295.     //  <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  296.     //  documents that the system grows the unit table by 4 entries
  297.     //  at a time.
  298. };
  299.  
  300. ///////////////////////////////////////////////////////////////////////////
  301.  
  302. static OSErr GrowUnitTable()
  303.     // This routine grows the unit table by kNumberOfEntriesToGrowUnitTable,
  304.     //  up to a maximum of kMaximumNumberOfUnitTableEntries.  The routine
  305.     //  is guaranteed to grow the table by at least one entry, or fail
  306.     //  with an error.
  307. {
  308.     OSErr        err;
  309.     Ptr         oldTable;
  310.     Ptr         newTable;
  311.     UInt32    oldCount;
  312.     UInt32    newCount;
  313.  
  314.     // Get the info about the old table, and calculate the new table size.    
  315.     oldTable = LMGetUTableBase();
  316.     oldCount = MoreLMGetUnitTableEntryCount();
  317.     newCount = oldCount + kNumberOfEntriesToGrowUnitTable;
  318.  
  319.     // Guard against growing the table too big.    
  320.     err = noErr;
  321.     if (newCount > kMaximumNumberOfUnitTableEntries) {
  322.         err = unitTblFullErr;
  323.     }
  324.     
  325.     // Allocate the new unit table in the system heap.  Note that we
  326.     //  clear the newly allocated memory, so that later on, when we
  327.     //  use this memory as the new unit table, the newly allocated
  328.     //  entries will be empty.
  329.  
  330.     if (err == noErr) {
  331.         newTable = NewPtrSysClear( newCount * sizeof(AuxDCEHandle));
  332.         err = MemError();
  333.     }
  334.  
  335.     // Copy the unit table entries over to the new table and then switch
  336.     //  to that table.  Note that this sequence doesn't disable interrupts,
  337.     //  instead it relies on the fact that programs can't modify the
  338.     //  unit table at interrupt time, and thus we, running at non-interrupt
  339.     //  time, have exclusive write access to the table.
  340.  
  341.     // Note that the sequence of these next few lines is *very* important.
  342.     //  If we did this in any other order, you could get to a situation
  343.     //  where interrupt code might be looking at an inconsistent 
  344.     //  unit table, which would be bad.
  345.     
  346.     // The sequence is:
  347.     //  1. copy the old unit table entries to the new table
  348.     //  2. change the unit table base pointer, so that interrupts
  349.     //         start using the new unit table
  350.     //  3. then change the unit table count, so that we have
  351.     //         more entries available
  352.     
  353.     if (err == noErr) {
  354.         BlockMoveData(oldTable, newTable, oldCount * sizeof(AuxDCEHandle));    // 1.
  355.         LMSetUTableBase(newTable);                                                                                    // 2.
  356.         MoreLMSetUnitTableEntryCount(newCount);                                                                    // 3.
  357.         
  358.         // Now its safe to dispose of the old unit table.
  359.         DisposePtr(oldTable);
  360.     }
  361.  
  362.     return err;
  363. }
  364.  
  365. ///////////////////////////////////////////////////////////////////////////
  366.  
  367. static OSErr FindFreeUnitNumber(UnitNumber beginningUnit,
  368.                                 UnitNumber endingUnit, 
  369.                                 UnitNumber *foundUnit)
  370.     // This routine walks the unit table looking for a free
  371.     //  slot.  The slot must be between beginningUnit
  372.     //  and endingUnit.  If endingUnit is greater than
  373.     //  TradHighestUnitNumber(), then we're allowed
  374.     //  to grow the unit table to meet our needs.
  375. {
  376.     OSErr err;
  377.     Boolean found;
  378.     UnitNumber currentUnit;
  379.     UnitNumber trueEndingUnit;
  380.     AuxDCEHandle *unitTable;
  381.     
  382.     unitTable = (AuxDCEHandle *) LMGetUTableBase();
  383.     
  384.     // Find trueEndingUnit, which is the minimum
  385.     //  of endingUnit and the highest unit number.
  386.     trueEndingUnit = endingUnit;
  387.     if ( trueEndingUnit > TradHighestUnitNumber() ) {
  388.         trueEndingUnit = TradHighestUnitNumber();
  389.     }
  390.  
  391.     // Scan through the unit table, starting at beginningUnit
  392.     //  and ending at trueEndingUnit, looking for an
  393.     //  empty slot.
  394.     currentUnit = beginningUnit;
  395.     found = false;
  396.     while (currentUnit <= trueEndingUnit && !found) {
  397.         found = (unitTable[currentUnit] == nil);
  398.         if (!found) {
  399.             currentUnit += 1;
  400.         }
  401.     }
  402.  
  403.     // Finish up.    
  404.     if (found) {
  405.         // We found an empty slot, return it.
  406.         *foundUnit = currentUnit;
  407.         err = noErr;
  408.     } else {
  409.  
  410.         // We didn't find an empty slot.  If we're
  411.         //  allowed to, grow the unit table, otherwise
  412.         //  just return an error.
  413.         
  414.         if (endingUnit > trueEndingUnit) {
  415.             err = GrowUnitTable();
  416.             if (err == noErr) {
  417.                 *foundUnit = trueEndingUnit + 1;
  418.             }
  419.         } else {
  420.             err = unitTblFullErr;
  421.         }
  422.     }
  423.     
  424.     return err;    
  425. }
  426.  
  427. ///////////////////////////////////////////////////////////////////////////
  428.  
  429. extern pascal OSErr TradInstallDriverFromPtr(DRVRHeaderPtr driver,
  430.                                                 UnitNumber beginningUnit,
  431.                                                 UnitNumber endingUnit,
  432.                                                 DriverRefNum *refNum)
  433.     // See comment in header file.
  434. {
  435.     OSErr err;
  436.     UnitNumber foundUnit;
  437.     AuxDCEHandle theDCE;
  438.     
  439.     // Sanity check parameters.
  440.     err = noErr;
  441.     if ( driver == nil ) {
  442.         err = paramErr;
  443.     }
  444.     if ( beginningUnit > TradHighestUnitNumber() ) {
  445.         err = badUnitErr;
  446.     }
  447.     if ( err == noErr && beginningUnit > endingUnit ) {
  448.         err = paramErr;
  449.     }
  450.     
  451.     // Check whether this driver is already installed.
  452.     if ( err == noErr ) {
  453.         // Check whether it's already installed.
  454.         foundUnit = IsDriverInstalled(&driver->drvrName[0], kNoUnitNumber);
  455.         if (foundUnit != kNoUnitNumber) {
  456.             // Return the refnum of the existing driver to the caller.
  457.             *refNum = ~foundUnit;
  458.             err = dupFNErr;
  459.         }
  460.     }
  461.     
  462.     // Now walk the unit table looking for a free slot.
  463.     if (err == noErr) {
  464.         err = FindFreeUnitNumber(beginningUnit, endingUnit, &foundUnit);
  465.     }
  466.  
  467.     // We've got a free slot, so let's install the device driver.
  468.     //  Note that we use DriverInstallReserveMem, rather than the standard
  469.     //  DriverInstall, so that the DCE is allocated low in the system
  470.     //  heap.  DriverInstallReserveMem was introduced with the 128K ROM.
  471.  
  472.     if (err == noErr) {
  473.         err = MoreDriverInstallReserveMem(driver, ~foundUnit);
  474.     }
  475.     
  476.     // Now do some important tidying up.
  477.     if (err == noErr) {
  478.  
  479.         // Return the refNum to the caller.
  480.         *refNum = ~foundUnit;
  481.  
  482.         theDCE = (AuxDCEHandle) GetDCtlEntry(*refNum);
  483.         
  484.         // Now setup the DCE properly.  There's a whole pile of things we
  485.         //  have to do, mainly because DriverInstall is such a brain-dead
  486.         //  routine.
  487.         
  488.         // First up, DriverInstall seems to ignore the first parameter
  489.         //  passed to it, so we have to blat the pointer to the driver code in
  490.         //  yourself afterwards.
  491.         
  492.         (**theDCE).dCtlDriver = (Ptr) driver;
  493.  
  494.         // Then we have to set up the flags.  We do this by copying the flags
  495.         //  out of the first word of the driver code.  We make sure to clear
  496.         //  the dRAMBased bit because we're actually a pointer-based driver
  497.         //  and DriverInstallReserveMem sets it to provisionally indicate that
  498.         //  we're a handle based driver.  We also set dNeedLock because
  499.         //  we want the the Device Manager to lock down the DCE.
  500.         
  501.         (**theDCE).dCtlFlags = (driver->drvrFlags & ~dRAMBasedMask) | dNeedLockMask;
  502.  
  503.         // There's also a bunch of fields we copy straight across without
  504.         //  any modification.  You might expect DriverInstall to copy
  505.         //  across these fields from the driver header to the DCE, but it doesn't
  506.         //  do that, so we do it ourselves.
  507.  
  508.         (**theDCE).dCtlDelay = driver->drvrDelay;
  509.         (**theDCE).dCtlEMask = driver->drvrEMask;
  510.         (**theDCE).dCtlMenu  = driver->drvrMenu;
  511.  
  512.         // Finally, we lock the DCE.
  513.         // Note that strictly speaking we don't need to HLock the DCE
  514.         //  because the Device Manager will do it when it you open a driver
  515.         //  that has dNeedLock set.  However, we want to
  516.         //  lock it now because DriverInstallReserveMem has just made sure
  517.         //  that the DCE was created low in the system heap, so we might as
  518.         //  well lock it down low rather than let it float.
  519.  
  520.         HLock( (Handle) theDCE );
  521.     }
  522.     
  523.     return err;
  524. }
  525.  
  526. ///////////////////////////////////////////////////////////////////////////
  527.  
  528. extern pascal OSErr TradInstallDriverFromHandle(DRVRHeaderHandle driver,
  529.                                                 UnitNumber beginningUnit,
  530.                                                 UnitNumber endingUnit,
  531.                                                 DriverRefNum *refNum)
  532.     // See comment in header file.
  533. {
  534.     OSErr err;
  535.     Size  driverSize;
  536.     DRVRHeaderPtr driverPtr;
  537.     
  538.     driverPtr = nil;
  539.     
  540.     err = noErr;
  541.     if (driver == nil || *driver == nil) {
  542.         err = paramErr;
  543.     }
  544.     if (err == noErr) {
  545.         driverSize = GetHandleSize( (Handle) driver );
  546.     }
  547.     if (err == noErr) {
  548.         driverPtr = (DRVRHeaderPtr) NewPtrSys( driverSize );
  549.         err = MemError();
  550.     }
  551.     
  552.     if (err == noErr) {
  553.         // This is *not* a BlockMoveData call. This time, we really are moving code!
  554.         //  I could have put cache flushing code in here, but then I would have
  555.         //  had to check whether it was available or not.
  556.         BlockMove( *driver, driverPtr, driverSize );
  557.         
  558.         err = TradInstallDriverFromPtr(driverPtr, beginningUnit, endingUnit, refNum);
  559.     }
  560.     
  561.     // Clean up.
  562.     if (err != noErr) {
  563.         // We're returning an error.  The API says we should leave the handle untouched,
  564.         //  but we should definitely clean up our new copy of the driver code.
  565.         if (driverPtr != nil) {
  566.             DisposePtr( (Ptr) driverPtr );
  567.         }
  568.     }
  569.     
  570.     return err;
  571. }
  572.  
  573. ///////////////////////////////////////////////////////////////////////////
  574.  
  575. extern pascal OSErr TradInstallDriverFromResource(SInt16 rsrcID, StringPtr rsrcName,
  576.                                                 UnitNumber beginningUnit,
  577.                                                 UnitNumber endingUnit,
  578.                                                 DriverRefNum *refNum)
  579.     // See comment in header file.
  580. {
  581.     OSStatus err;
  582.     Handle driverHandle;
  583.     
  584.     // Note: We don't care which zone the resource gets loaded, because 
  585.     //  TradInstallDriverFromHandle makes a copy of it anyway.
  586.  
  587.     // Get the resource, using either rsrcID or rsrcName.
  588.     if (rsrcName == nil) {
  589.         driverHandle = Get1Resource('DRVR', rsrcID);
  590.     } else {
  591.         driverHandle = Get1NamedResource('DRVR', rsrcName);
  592.     }
  593.     
  594.     // Set err if we couldn't get the resource.
  595.     if (driverHandle == nil) {
  596.         err = ResError();
  597.         if (err == noErr) {
  598.             err = resNotFound;
  599.         }
  600.     } else {
  601.         // Make sure we're not killed by some clown making the 'DRVR' purgeable.
  602.         HNoPurge(driverHandle);                    
  603.         err = MemError();
  604.     }
  605.     
  606.     // Now install the driver as if we'd got it from a memory handle.    
  607.     if (err == noErr) {
  608.         err = TradInstallDriverFromHandle( (DRVRHeaderHandle) driverHandle, beginningUnit, endingUnit, refNum);
  609.  
  610.         ReleaseResource(driverHandle);
  611.         if (err == noErr) {
  612.             err = ResError();
  613.         }
  614.     }
  615.     
  616.     return err;
  617. }
  618.  
  619. ///////////////////////////////////////////////////////////////////////////
  620.  
  621. extern pascal OSErr TradGetDriverInformation(DriverRefNum refNum,
  622.                                                 UnitNumber *thisUnit,
  623.                                                 DriverFlags *flags,
  624.                                                 StringPtr name,
  625.                                                 DRVRHeaderPtr *driverHeader
  626.                                                 )
  627.     // See comment in header file.
  628. {
  629.     OSErr err;
  630.     UnitNumber             tmpUnit;
  631.     AuxDCEHandle        tmpDCE;
  632.     DRVRHeaderPtr        tmpHeader;
  633.     DRVRHeaderHandle    tmpDriverHandle;
  634.     
  635.     // Get some initial information.
  636.     tmpUnit = ~refNum;
  637.     
  638.     // Sanity check the refNum parameter.
  639.     err = noErr;
  640.     if (tmpUnit > TradHighestUnitNumber()) {
  641.         err = badUnitErr;
  642.     }
  643.     if (err == noErr) {
  644.         tmpDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  645.         if ( tmpDCE == nil ) {
  646.             err = unitEmptyErr;
  647.         }
  648.     }
  649.     if (err == noErr) {
  650.         if ( (*tmpDCE == nil) || (GetHandleSize( (Handle) tmpDCE) < sizeof(DCtlEntry)) ) {
  651.             err = dceExtErr;
  652.         }
  653.     }
  654.     
  655.     // Get the information from the DCE.
  656.     if (err == noErr) {
  657.  
  658.         // From the DCE, find the DRVR header.  This can fail for a number of reasons:
  659.         //     1. dCtlDriver is nil
  660.         //     2. the driver is handle based, and the handle's master point is nil
  661.         //     3. the driver is handle based, and the driver's handle is too small
  662.         // In all of these cases, we set tmpHeader to nil, returning nil to our
  663.         // client.
  664.         
  665.         tmpHeader = (DRVRHeaderPtr) (**tmpDCE).dCtlDriver;
  666.         if ( tmpHeader != nil ) {
  667.             if ( ((**tmpDCE).dCtlFlags & dRAMBasedMask) != 0 ) {
  668.  
  669.                 tmpDriverHandle = (DRVRHeaderHandle) tmpHeader;
  670.                 
  671.                 if ( (*tmpDriverHandle != nil) &&
  672.                             (GetHandleSize( (Handle) tmpDriverHandle) >= sizeof(DRVRHeader)) ) {
  673.                     tmpHeader = *tmpDriverHandle;
  674.                 }
  675.             }
  676.         }
  677.         
  678.         // Now copy out the various requested parameters
  679.         if (thisUnit != nil) {
  680.             *thisUnit = tmpUnit;
  681.         }
  682.         if (flags != nil) {
  683.             *flags = (**tmpDCE).dCtlFlags;
  684.         }
  685.         if (name != nil) {
  686.             if ( tmpHeader == nil ) {
  687.                 name[0] = 0;
  688.             } else {
  689.                 BlockMoveData(&tmpHeader->drvrName[0], name, tmpHeader->drvrName[0] + 1);
  690.             }
  691.         }
  692.         if (driverHeader != nil) {
  693.             *driverHeader = tmpHeader;
  694.         }
  695.     }
  696.     
  697.     return err;
  698. }
  699.  
  700. ///////////////////////////////////////////////////////////////////////////
  701.  
  702. // Some extra dCtlFlags bits, as documented in:
  703. // <http://developer.apple.com/qa/dv/dv27.html>
  704.  
  705. enum {
  706.     dNativeDriverMask = 0x0008
  707. };
  708.  
  709. ///////////////////////////////////////////////////////////////////////////
  710.  
  711. extern pascal OSErr TradRemoveDriver(DriverRefNum refNum, Boolean immediate)
  712.     // See comment in header file.
  713. {
  714.     OSErr                 err;
  715.     DriverFlags     flags;
  716.     DRVRHeaderPtr driverHeader;
  717.  
  718.     // Check parameters.
  719.     err = noErr;
  720.     if (immediate) {
  721.         err = paramErr;
  722.     }
  723.  
  724.     // Get information about the driver we're closing.
  725.     if (err == noErr) {
  726.         err = TradGetDriverInformation(refNum, nil, &flags, nil, &driverHeader);
  727.     }
  728.     if (err == noErr) {
  729.         if ( driverHeader == nil || ((flags & dNativeDriverMask) != 0)) {
  730.             err = paramErr;
  731.         }
  732.     }
  733.     
  734.     // If the driver is open, close it.
  735.     if (err == noErr) {
  736.         if ( (flags & dOpenedMask) != 0 ) {
  737.             err = CloseDriver(refNum);
  738.         }
  739.     }
  740.     
  741.     // Now call the system to remove the driver from the unit table.  Note that this
  742.     //  works because of a subtlety in DriverRemove.  If the driver being removed
  743.     //  is a RAM-based driver (which our drivers aren't), DriverRemove will call
  744.     //  ReleaseResource on the dCtlDriver.  We don't want this, so we make our drivers
  745.     //  not RAM-based.
  746.     
  747.     if (err == noErr) {
  748.         err = DriverRemove(refNum);
  749.     }
  750.     
  751.     if (err == noErr) {
  752.         // All is cool, so let's dispose of the code.
  753.         DisposePtr( (Ptr) driverHeader);
  754.     }
  755.     
  756.     return err;
  757. }
  758.  
  759. ///////////////////////////////////////////////////////////////////////////
  760.  
  761. extern pascal OSErr TradRenameDriver(DriverRefNum refNum, ConstStr255Param newDriverName)
  762.     // See *important* comment in header file.
  763. {
  764.     OSErr             err;
  765.     Str255             driverName;
  766.     DRVRHeaderPtr     driverHeader;
  767.     DriverFlags        flags;
  768.     
  769.     err = noErr;
  770.     if ( newDriverName[0] == 0 ) {
  771.         err = paramErr;
  772.     }
  773.     if (err == noErr) {
  774.         // Get information about the driver we're renaming.
  775.         err = TradGetDriverInformation(refNum, nil, &flags, driverName, &driverHeader);
  776.     }
  777.     if (err == noErr) {
  778.         if ( driverHeader == nil || ((flags & dNativeDriverMask) != 0)) {
  779.             err = paramErr;
  780.         }
  781.     }
  782.     
  783.     // Now check the name lengths.  See comment in implementation for details.
  784.     if (err == noErr) {
  785.         if ( newDriverName[0] > driverName[0] ) {
  786.             err = paramErr;
  787.         }
  788.     }
  789.     
  790.     // Now check whether the new name is already present in the unit table.
  791.     if (err == noErr) {
  792.         if ( IsDriverInstalled(newDriverName, ~refNum) != kNoUnitNumber ) {
  793.             err = dupFNErr;
  794.         }
  795.     }
  796.     
  797.     // Now copy in the new driver name.
  798.     if (err == noErr) {
  799.         BlockMoveData( newDriverName, &driverHeader->drvrName[0], newDriverName[0] + 1 );
  800.     }
  801.     
  802.     return err;
  803. }
  804.